package com.inexas.cu.stratus;

import static org.junit.Assert.*;
import java.util.*;
import org.junit.*;
import com.inexas.util.*;

public class BuildTestConstraints {
	private static class Entity {
		public final static Date referenceDate =	//
			DateUtilities.toDate(DateUtilities.toDateTimeString(new Date()));
		final boolean booleanField;
		final byte byteField;
		final char characterField;
		final double doubleField;
		final float floatField;
		final int integerField;
		final long longField;
		final short shortField;
		
		final Boolean aBooleanField;
		final Byte aByteField;
		final Character aCharacterField;
		final Double aDoubleField;
		final Float aFloatField;
		final Integer aIntegerField;
		final Long aLongField;
		final Short aShortField;
		
		final Date dateField;
		final String stringField;


		public Entity(String stringField, int integerField) {
			booleanField = integerField > 0;
			aBooleanField = new Boolean(booleanField);
			byteField = (byte)integerField;
			aByteField = new Byte(byteField);
			characterField = (char)integerField;
			aCharacterField = new Character(characterField);
			doubleField = integerField;
			aDoubleField = new Double(doubleField);
			floatField = integerField;
			aFloatField = new Float(floatField);
			this.integerField = integerField;
			aIntegerField = new Integer(integerField);
			longField = integerField;
			aLongField = new Long(longField);
			shortField = (short)integerField;
			aShortField = new Short(shortField);
			
			this.stringField = stringField;
			this.dateField = new Date(referenceDate.getTime() + integerField * 1000);
		}

		@Override
        public String toString() {
			return "TE-" + integerField;
        }
	}	
	
	public final static Entity a_0, b_1, c_2;
	static {
		a_0 = new Entity("a", 0);
		b_1 = new Entity("b", 1);
		c_2 = new Entity("c", 2);
	}
	private List<Entity> entities;
	
	private void testNumericType(String name) {
		testFilter(new String[] { name + "Field:1" }, a_0, false);
		testFilter(new String[] { name + "Field:1" }, b_1, true);

		testFilter(new String[] { objectName(name) + "Field:1" }, a_0, false);
		testFilter(new String[] { objectName(name) + "Field:1" }, b_1, true);

		test(new String[] { name + "Field+" }, 0, 1, 2);
		test(new String[] { name + "Field-" }, 2, 1, 0);

		test(new String[] { objectName(name) + "Field+" }, 0, 1, 2);
		test(new String[] { objectName(name) + "Field-" }, 2, 1, 0);
	}

	private String objectName(String name) {
	    return "a" + Character.toUpperCase(name.charAt(0)) + name.substring(1);
    }
	
	private void test(String[] constraints, int... result) {
		final Constraints<Entity> c = new Constraints<Entity>(constraints);
		final List<Entity> constrained = c.constrain(entities);
		assertTrue(constrained.size() == result.length);
		for(int i = 0; i < constrained.size(); i++) {
			assertTrue(constrained.get(i).integerField == result[i]);
		}
    }
	
	private void testFilter(String[] constraints, Entity entity, boolean result) {
		assert((new Constraints<Entity>(constraints)).accepts(entity) == result);
    }
	
	@Before
	public void setup() {
		entities = new ArrayList<Entity>();
		entities.add(a_0);
		entities.add(b_1);
		entities.add(c_2);
	}
	
	@Test
	public void filterEqualName() {
		testFilter(new String[] {"stringField:'b'"}, a_0, false);
		testFilter(new String[] {"stringField:'b'"}, b_1, true);
		testFilter(new String[] {"stringField:'b'"}, c_2, false);

		testFilter(new String[] {"stringField:\"a\""}, a_0, true);
		testFilter(new String[] {"stringField:\"a\""}, b_1, false);
		testFilter(new String[] {"stringField:\"a\""}, c_2, false);
	}

	@Test
	public void filterEqualIntegerField() {
		testFilter(new String[] { "integerField:1" }, a_0, false);
		testFilter(new String[] { "integerField:1" }, b_1, true);
		testFilter(new String[] { "integerField:1" }, c_2, false);

		testFilter(new String[] { "integerField:2" }, a_0, false);
		testFilter(new String[] { "integerField:2" }, b_1, false);
		testFilter(new String[] { "integerField:2" }, c_2, true);
	}

	@Test
	public void sortVarious() {
		test(new String[] { "stringField" }, 0, 1, 2);
		test(new String[] { "stringField+" }, 0, 1, 2);
		test(new String[] { "stringField-" }, 2, 1, 0);
		test(new String[] { "dateField+" }, 0, 1, 2);
		test(new String[] { "dateField-" }, 2, 1, 0);
	}
	
	@Test
	public void allTypes() {
		///testNumericType("byte");
		testNumericType("character");
		testNumericType("double");
		testNumericType("float");
		testNumericType("integer");
		testNumericType("long");
		testNumericType("short");

		final Constraints<Entity> falseOnly = new Constraints<Entity>("booleanField:false");
		assertTrue(falseOnly.accepts(a_0));
		assertFalse(falseOnly.accepts(b_1));
		
		final Constraints<Entity> trueOnly = new Constraints<Entity>("booleanField:true");
		assertFalse(trueOnly.accepts(a_0));
		assertTrue(trueOnly.accepts(b_1));
		
		final long nowPlus1 = Entity.referenceDate.getTime() + 1000;
		final String nowPlus1AsString =	DateUtilities.toDateTimeString(new Date(nowPlus1));
		final Constraints<Entity> nowPlus1Only =	//
			new Constraints<Entity>("dateField:'" + nowPlus1AsString + '\'');
		assertFalse(nowPlus1Only.accepts(a_0));
		assertTrue(nowPlus1Only.accepts(b_1));
	}

	@Test
	public void conditionals() {
		test(new String[] { "integerField < 1" }, 0);
		test(new String[] { "integerField <= 1" }, 0, 1);
		test(new String[] { "integerField == 1" }, 1);
		test(new String[] { "integerField >= 1" }, 1, 2);
		test(new String[] { "integerField > 1" }, 2);
		test(new String[] { "booleanField:true" }, 1, 2);
	}

	@Test
	public void multiples() {
		test(new String[] { "booleanField+", "integerField+" }, 0, 1, 2);
		test(new String[] { "booleanField+", "integerField-" }, 0, 2, 1);
		test(new String[] { "booleanField-", "integerField-" }, 2, 1, 0);
		test(new String[] { "booleanField-", "integerField+" }, 1, 2, 0);

		test(new String[] { "integerField<2", "integerField<1" }, 0);
		test(new String[] { "integerField>1", "integerField>0" }, 2);
		
		test(new String[] { "booleanField:true", "integerField" }, 1, 2);
	}

}
